#include "Game.h"

Game::Game()
	: activePlayer(0)
	, selectionIndex(-1)
	, P1XStart(-130)
	, P1YStart(-90)
	, P2XStart(110)
	, P2YStart(130)
	, pieceRows(3)
	, pieceColumns(7)
	, xSpacing(40)
	, zSpacing(60)
	, m_displayInstructions(false)
	, pGuideText(NULL)
	, pP1Score(NULL)
	, pP2Score(NULL)
	, pPassButton(NULL)
{

	// initial all the camera positions
	m_v3P1PieceView.x =	   0;
	m_v3P1PieceView.y =	 250;
	m_v3P1PieceView.z =	-350;

	m_v3P1TopDownView	= m_v3P1PieceView;
	m_v3P2PieceView		= m_v3P1PieceView;
	m_v3P2TopDownView	= m_v3P1PieceView;

	m_v3P2PieceView.z	=  350;
	m_v3P1TopDownView.z = -100;
	m_v3P2TopDownView.z =  100;
}

// creates a new set of pieces and game board
// also resets scores and other flags
void Game::StartNewGame()
{
	Release();
	theState = SELECTION;
	// default scores and passes to 0, and have player's start on first turn
	for(int i = 0; i < 2; ++i)
	{
		m_Players[i].myScore = 0;
		m_Players[i].passes = 0;
		m_Players[i].firstTurn = true;
	}

	selectionIndex = 0;
	activePlayer = PLAYER1;

	Piece* temppieces[numPieces];
	Piece* temppieces2[numPieces];

	int iter = 0;

	// initialize the pieces including their starting positions
	for(int z = 0; z < pieceRows; z++)
	{
		for(int x = 0; x < pieceColumns; x++, iter++)
		{
			/// Player 1's pieces
			temppieces[iter] = new Piece;
			temppieces[iter]->Init(iter,					
					   PLAYER1,										// the player who owns these pieces
					   Vec3D((float)(P1XStart+(xSpacing*x)), 0, (float)(P1YStart-(zSpacing*z)))); // aligns the rows and columns of pieces nice and proper like

			m_Players[0].myPieces.push(temppieces[iter]);			// give the piece to the player

			/// Player 2's pieces
			temppieces2[iter] = new Piece;
			temppieces2[iter]->Init(iter,					
					   PLAYER2,										
					   Vec3D((float)(P2XStart-(xSpacing*x)), 0, (float)(P2YStart+(zSpacing*z)))); 
			m_Players[1].myPieces.push(temppieces2[iter]);
		}
	}
	pCamera = new Drawable;
	pCamera->Init(CAMERA, NOPLAYER, m_v3P1PieceView);

	// initialize a buffer to use with itoa
	char buffer[10];
	_itoa(m_Players[0].myScore, buffer, 10);

	pP1Score->ChangeText("P1 Score: ");
	pP1Score->AppendText(buffer);

	_itoa(m_Players[1].myScore, buffer, 10);
	pP2Score->ChangeText("P2 Score: ");
	pP2Score->AppendText(buffer);
}

bool Game::Init()
{
	m_theBoard.Init(BOARD, NOPLAYER, Vec3D(0, 0, 0));

	// create temporary RECTS to define the textbox locations
	RECT scoreLoc1, scoreLoc2;
	scoreLoc1.top = 300;
	scoreLoc1.bottom = 350;
	scoreLoc1.left = 0;
	scoreLoc1.right = 20;
	
	scoreLoc2 = scoreLoc1;
	scoreLoc2.left = 700;
	scoreLoc2.right = 800;

	// initialize text boxes
	pGuideText = new TextBox;
	pGuideText->Init();
	pP1Score = new TextBox;
	pP1Score->Init(scoreLoc1);
	pP2Score = new TextBox;
	pP2Score->Init(scoreLoc2);

	// initialize pass/quit button
	scoreLoc1.top -= 200;
	scoreLoc1.bottom -= 200;
	pPassButton = new Button;
	if(pPassButton)
	{
		pPassButton->Init(scoreLoc1);
		//pPassButton->ChangeText("Click Here to Skip Turn");
	}

	StartNewGame();
	return true;
}

Vector<Piece*> Game::getPieces()
{
	Vector<Piece*> vPieces;
	for(int p = 0; p  < 2; ++p)
	{
		for(int i = 0; i < m_Players[p].myPieces.sizeOf(); ++i)
		{
			// if pieces have a new shape (because of rotation or mirroring), 
			if(m_Players[p].myPieces[i]->m_bUpdatedShape)
			{
				vPieces.push(m_Players[p].myPieces[i]);
				m_Players[p].myPieces[i]->m_bUpdatedShape = false;
			}
		}
	}

	return vPieces;
}

Vector<Drawable*> Game::getDrawables()
{
	Vector<Drawable*> toBeDrawn;	// create a new vector of all the objects to be drawn
	if(theState != GAMEOVER)
	{
		for(int i = 0; i < 2; i++)
		{
			for(int j = 0; j < m_Players[i].myPieces.sizeOf(); j++)	// push player 1's pieces into the vector
				toBeDrawn.push(m_Players[i].myPieces[j]);
		}

		for(int i = 0; i < m_theBoard.vPieces.sizeOf(); i++)
			toBeDrawn.push(m_theBoard.vPieces[i]);

		toBeDrawn.push(&m_theBoard);	// push the game board in the vector

		toBeDrawn.push(pCamera);
	}
	return toBeDrawn;
}

Vector<TextBox*> Game::getTextBoxes()
{
	Vector<TextBox*> toBeDrawn;
	// draw all the normal text boxes and buttons as long as it's not the game over state
	if(theState != GAMEOVER)
	{
		toBeDrawn.push(pGuideText);
		toBeDrawn.push(pP1Score);
		toBeDrawn.push(pP2Score);

		if(!m_Players[activePlayer].firstTurn && theState == SELECTION)
			toBeDrawn.push(pPassButton);
	}

	return toBeDrawn;
}

void Game::HandleSelectionInput()
{
	// rotate the piece if R is pressed
	if(DInput::Engine()->keyPressed(DIK_R))
	{
		m_Players[activePlayer].myPieces[selectionIndex]->Rotate(LEFT);
	}

	// if the skip button was pressed and it isn't the player's first turn
	if(pPassButton->WasClicked() && !m_Players[activePlayer].firstTurn)
	{
		m_Players[activePlayer].passes++;
		if(m_Players[activePlayer].passes <= 3)
			activePlayer = !activePlayer;
	}

	// if the user presses enter, change to PLACEMENT state
	if(DInput::Engine()->keyPressed(DIK_RETURN))
	{
		theState = PLACEMENT;
		Sound::Engine()->PlayEffect(SELECTION);

		// if this is the first turn, mark the piece as their first one
		if(m_Players[activePlayer].firstTurn)
			m_Players[activePlayer].myPieces[selectionIndex]->m_bFirstPiece = true;
	}

	if(m_Players[activePlayer].passes == 3)
		pPassButton->ChangeText("Click Here to Forfeit");
	else
		pPassButton->ChangeText("Click Here To Skip Turn");
}

bool Game::Update()
{
	if(DInput::Engine()->keyPressed(DIK_I))
		m_displayInstructions = !m_displayInstructions;

	if(m_Players[activePlayer].passes > 3)
		theState = GAMEOVER;

	switch(theState)
	{
	case SELECTION:
		// allow the user to go through each piece and select one to play
		Selection(m_Players[activePlayer], selectionIndex);

		HandleSelectionInput();

		// sets instructions depending on the player
		if(activePlayer == 0)
		{
			pCamera->setPos(m_v3P1PieceView);

			if(m_Players[activePlayer].firstTurn || m_displayInstructions)
				pGuideText->ChangeText("Use the W-A-S-D keys to select a piece.");
			else
				pGuideText->ChangeText("");
		}
		else
		{
			pCamera->setPos(m_v3P2PieceView);

			if(m_Players[activePlayer].firstTurn || m_displayInstructions)
				pGuideText->ChangeText("Use the arrow keys to select a piece.");
			else
				pGuideText->ChangeText("");
		}

		break;
	case PLACEMENT:
		// allows the user to place the piece he selected where he wants on the board
		Placement(m_Players[activePlayer].myPieces[selectionIndex]);
		
		// if user wants to place piece on the board and end turn 
		if(DInput::Engine()->keyPressed(DIK_RETURN))
		{
			// play the sound effect for placing a piece on the board
			Sound::Engine()->PlayEffect(PLACE);

			// if the current spot on the board is a valid place
			if(m_theBoard.placePiece(m_Players[activePlayer].myPieces[selectionIndex]))
			{
				// update score
				int type = m_Players[activePlayer].myPieces[selectionIndex]->m_iMeshIndex;
				int size;

				// determine the amount of blocks in the piece
				if(type < DOM)			size = 1;
				else if(type < TRI0)	size = 2;
				else if(type < TETR0)	size = 3;
				else if(type < PENT0)	size = 4;
				else					size = 5;

				m_Players[activePlayer].myScore += size;

				// update the score display
				char buffer[10];
				_itoa(m_Players[activePlayer].myScore, buffer, 10);
				if(activePlayer==0)
				{
					pP1Score->ChangeText("P1 Score: ");
					pP1Score->AppendText(buffer);
				}
				else
				{
					pP2Score->ChangeText("P2 Score: ");
					pP2Score->AppendText(buffer);
				}

				m_Players[activePlayer].myPieces.remove(selectionIndex);
				m_Players[activePlayer].myPieces[selectionIndex]->m_bSelected = false;

				if(m_Players[activePlayer].firstTurn)
				{
					m_Players[activePlayer].myPieces[selectionIndex]->m_bFirstPiece = false;
					m_Players[activePlayer].firstTurn = false;
				}
				theState = SELECTION;
				activePlayer = !activePlayer;
				selectionIndex = 0;

				pGuideText->ChangeText("");
			}
			else
			{
				pGuideText->ChangeText("The piece must connect at the corners with another of your piece\n"
								      "The piece cannot touch another of yours directly though");
				Sound::Engine()->PlayEffect(BADPLACE);
			}
		}

		// if the player wants to go back and select a different piece
		else if(DInput::Engine()->keyPressed(DIK_BACKSPACE))
		{
			theState = SELECTION;

			// if they have yet to make their first turn, then unmark the current piece as not being the first
			if(m_Players[activePlayer].firstTurn)
				m_Players[activePlayer].myPieces[selectionIndex]->m_bFirstPiece = false;
			//ReturnPiece(activePlayer, selectionIndex);
			m_Players[activePlayer].myPieces[selectionIndex]->Return();
		}

		if(activePlayer == 0)
		{
			pCamera->setPos(m_v3P1TopDownView);
			if(m_Players[activePlayer].firstTurn || m_displayInstructions)
				pGuideText->ChangeText("WASD keys to move\nEnter to place the piece on the board\nBackspace to select a different piece");
		}
		else
		{
			pCamera->setPos(m_v3P2TopDownView);
			if(m_Players[activePlayer].firstTurn || m_displayInstructions)
				pGuideText->ChangeText("Arrow keys to move\nEnter to place the piece on the board\nBackspace to select a different piece");
		}
		break;
	case GAMEOVER:
		if(DInput::Engine()->keyPressed(DIK_N))
			StartNewGame();
		break;
	}
	return true;
}

void Game::Selection(Player & player, int & selectionIndex)
{
	// if the selected index has not yet been set, set it to 0 by default
		if(selectionIndex == -1)
		{
			selectionIndex = 0;
		}

		bool buttonHit = false;

		if(activePlayer == PLAYER1)
		{
			if(DInput::Engine()->keyPressed(DIK_D))
			{
				buttonHit = true;
				player.myPieces[selectionIndex++]->m_bSelected = false;
			}
			if(DInput::Engine()->keyPressed(DIK_A))
			{
				buttonHit = true;
				player.myPieces[selectionIndex--]->m_bSelected = false;
			}
			if(DInput::Engine()->keyPressed(DIK_W))
			{
				buttonHit = true;
				player.myPieces[selectionIndex]->m_bSelected = false;
				selectionIndex -= pieceColumns;
			}
			if(DInput::Engine()->keyPressed(DIK_S))
			{
				buttonHit = true;
				player.myPieces[selectionIndex]->m_bSelected = false;
				selectionIndex += pieceColumns;
			}
		}
		else
		{
			if(DInput::Engine()->keyPressed(DIK_RIGHT))
			{
				buttonHit = true;
				player.myPieces[selectionIndex++]->m_bSelected = false;
			}
			if(DInput::Engine()->keyPressed(DIK_LEFT))
			{
				buttonHit = true;
				player.myPieces[selectionIndex--]->m_bSelected = false;
			}
			if(DInput::Engine()->keyPressed(DIK_UP))
			{
				buttonHit = true;
				player.myPieces[selectionIndex]->m_bSelected = false;
				selectionIndex -= pieceColumns;
			}
			if(DInput::Engine()->keyPressed(DIK_DOWN))
			{
				buttonHit = true;
				player.myPieces[selectionIndex]->m_bSelected = false;
				selectionIndex += pieceColumns;
			}
		}

		if(buttonHit)
		{
			Sound::Engine()->PlayEffect(SELECT);
		}

		if(selectionIndex < 0)
			selectionIndex = 0;
		else if(selectionIndex >= player.myPieces.sizeOf())
			selectionIndex = player.myPieces.sizeOf() - 1;

		player.myPieces[selectionIndex]->m_bSelected = true;
}

bool Game::Placement(Piece* pPiece)
{
	Direction pieceDir;
	Board* b = &this->m_theBoard;
	Vec3D originalPos = pPiece->getPos();
	// move the piece from the player's area to the game board if necessary
	if(pPiece->getPos().z <= P1YStart ||
		pPiece->getPos().z >= P1YStart * -1)
	{
		// move the piece to starting position if it's the first turn
		if(pPiece->m_bFirstPiece == true)
		{
			PlaceStartingPiece(pPiece, pPiece->owner);
		}

		//otherwise place it on the board regularly
		else 
		{
			int xPos, zPos;
			// search through the map to find a starting position close to the center
			// if one cannot be found, return false, to signal the game to end
			Piece temp(*pPiece);
 			m_theBoard.findEmptySpot(temp, 2, xPos, zPos);

			if(xPos == -1 || zPos == -1)
			{
				// Search the the rest of player pieces to make sure that one is still playable
				for(int i = 0; i < m_Players[activePlayer].myPieces.sizeOf(); ++i)
				{
					temp = *(m_Players[activePlayer].myPieces[i]);
					m_theBoard.findEmptySpot(temp, 2, xPos, zPos);
				}
			}
			else
			{
				pPiece->MoveTo((float)xPos, (float)zPos);
			}
		}
	}

	// player one uses WASD keys to move
	if(pPiece->owner == PLAYER1)
	{
		if(DInput::Engine()->keyPressed(DIK_W))			pieceDir.up = true;
		else if(DInput::Engine()->keyPressed(DIK_S))		pieceDir.down = true;

		if(DInput::Engine()->keyPressed(DIK_D))			pieceDir.right = true;
		else if(DInput::Engine()->keyPressed(DIK_A))		pieceDir.left = true;
	}

	// player two uses inverted arrow keys since the map is backwards
	else
	{
		if(DInput::Engine()->keyPressed(DIK_UP))			pieceDir.down = true;
		else if(DInput::Engine()->keyPressed(DIK_DOWN))	pieceDir.up = true;

		if(DInput::Engine()->keyPressed(DIK_RIGHT))		pieceDir.left = true;
		else if(DInput::Engine()->keyPressed(DIK_LEFT))	pieceDir.right = true;
	}

	if(pieceDir.left || pieceDir.right || pieceDir.up || pieceDir.down)
	{
		Sound::Engine()->PlayEffect(MOVE);
	}

	pPiece->Move(pieceDir);						// move the piece in the direction desired
	if(m_theBoard.isOutOfBounds(pPiece))		// if it moved out of bounds, bring it back (collision by forgiveness)
		pPiece->MoveTo(originalPos.x, originalPos.z);

	// if the new location is not valid, just keep trying to move it that direction until a valid one is found
	while(!m_theBoard.isValidSpot(pPiece))
	{
		pPiece->Move(pieceDir);

		// however, if a valid spot is not available in that direction, move back to where it started and break
		if(m_theBoard.isOutOfBounds(pPiece))
		{
			pPiece->MoveTo(originalPos.x, originalPos.z);
			break;
		}
	}
	return true;
}

// based on the player and the piece index, determines where the pieces original position was and places it back there
void Game::ReturnPiece(int playerIndex, int selectionIndex)
{
	int zPos = selectionIndex / pieceColumns;			// first find what row it should be in
	int xPos = selectionIndex - (zPos * pieceColumns);	// using the above, find what column it should be in

	// use the player's starting coordinates to determine what position in world space the piece should be in
	if(playerIndex == PLAYER1)
	{
		xPos = P1XStart+(xSpacing*xPos);
		zPos = P1YStart-(zSpacing*zPos);
	}
	else
	{
		xPos = P2XStart-(xSpacing*xPos);
		zPos = P2YStart+(zSpacing*zPos);
	}

	// move the player to that spot
	m_Players[playerIndex].myPieces[selectionIndex]->MoveTo((float)xPos, (float)zPos);
}

void Game::PlaceStartingPiece(Piece* pPiece, int player)
{
	int xPos;
	int zPos;

	// move to their specified starting area
	if(player == PLAYER1)
	{
		xPos = (int)m_theBoard.v3P1Start.x;
		zPos = (int)m_theBoard.v3P1Start.z;
	}
	else
	{
		xPos = (int)m_theBoard.v3P2Start.x;
		zPos = (int)m_theBoard.v3P2Start.z;
	}
	pPiece->MoveTo((float)xPos, (float)zPos);

	m_theBoard.WorldCoordToArrayIndex(xPos, zPos);

	if(pPiece->m_bppShape[0][0] == false)
	{
		Direction dir;
		dir.left = true;
		pPiece->Move(dir);
	}
}

void Game::Release()
{
	for(int i = 0; i < 2; i++)
		m_Players[i].myPieces.release();


	m_theBoard.Cleanup();
}

Game::~Game()
{
	Release();

	if(pGuideText)
		delete pGuideText;
	if(pP1Score)
		delete pP1Score;
	if(pP2Score)
		delete pP2Score;
	if(pCamera)
		delete pCamera;
}